package svc.elib.analysis;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;

import svc.elib.db.Author;
import svc.elib.db.Database;
import svc.elib.db.Paper;
import svc.elib.socnet.ConnectedComponents;
import svc.elib.socnet.Net;
import svc.elib.socnet.SocConstructor;
import svc.elib.util.Distribution;


/**
 * Research questions
 *   1. The distribution of component sizes
 *   2. Size of the component <--> num papers
 *   3. Fractal dimension of the component
 *   4. Small world coefficient of the component
 *   5. Clustering coefficient of the component
 *   6. ratio (male/female) authors
 *   
 * Component vector
 * 
 * @author svc
 */
public class ConnectedComponentAnalysis {

	private static int determineNumberOfPapers(Net n) {
		Iterator<Author> it = n.getGraph().getVertices().iterator();
		HashSet<String> paperSet = new HashSet<String>();
		
		while (it.hasNext()) {
			Author a = it.next();
			LinkedList<Paper> papers = a.getPapers();
			for (int i = 0; i < papers.size(); i++) {
				Paper p = papers.get(i);
				String pid = p.getId();
				paperSet.add(pid);
			}
		}
	
		return paperSet.size();
	}
	
	private static int determineNumberOfJournals(Net n) {
		Iterator<Author> it = n.getGraph().getVertices().iterator();
		HashSet<String> journalSet = new HashSet<String>();
		
		while (it.hasNext()) {
			Author a = it.next();
			LinkedList<Paper> papers = a.getPapers();
			for (int i = 0; i < papers.size(); i++) {
				Paper p = papers.get(i);
				String jid = p.getJournal();
				journalSet.add(jid);
			}
		}
	
		return journalSet.size();
	}
	
	private static int determineSpan(Net n) {
		Iterator<Author> it = n.getGraph().getVertices().iterator();
		int max = Integer.MIN_VALUE;
		int min = Integer.MAX_VALUE;
		
		while (it.hasNext()) {
			Author a = it.next();
			LinkedList<Paper> papers = a.getPapers();
			for (int i = 0; i < papers.size(); i++) {
				Paper p = papers.get(i);
				int year = p.getYear();
				if (year > max)
					max = year;
				else
				if (year < min)
					min = year;
			}
		}
	
		return max - min + 1;
	}
	
	private static int determineNumberOfPapersWrittenByIsolatedAuthors(Net net) {
		int sum = 0;
		Iterator<Author> ait = net.getGraph().getVertices().iterator();
		while (ait.hasNext()) {
			Author a = ait.next();
			if (net.getGraph().degree(a) == 0) {
				sum += a.getPapers().size();
			}
		}
		
		return sum;
	}
	
	private static void updateMap(HashMap<Integer, LinkedList<Double>> map, int x, double y) {
		LinkedList<Double> yVals = map.get(x);
		if (yVals == null) {
			yVals = new LinkedList<Double>();
			yVals.add(y);
			map.put(x, yVals);
		} else {
			yVals.add(y);
		}
	}
	
	private static double avg(LinkedList<Double> list) {
		double sum = 0.0;
		for (int i = 0; i < list.size(); i++) {
			sum += list.get(i);
		}
		return sum / (double) list.size();
	}
	
	private static void dumpMap(HashMap<Integer, LinkedList<Double>> map) {
		Iterator<Entry<Integer, LinkedList<Double>>> it = map.entrySet().iterator();
		while (it.hasNext()) {
			Entry<Integer, LinkedList<Double>> e = it.next();
			int x = e.getKey();
			double y = avg(e.getValue());
			System.out.println(x + ", " + y);
		}
	}
	
	public static void main(String[] args) 
		throws IOException
	{
		Database db = new Database("eLibData.csv", 1932, 2011);
		SocConstructor soc = new SocConstructor(db);
		Net net = soc.getNet();
		ConnectedComponents cn = new ConnectedComponents(net);
		cn.resolveComponents();
		LinkedList<Net> components = cn.getComponents();
		int numTrivialComponents = 0;
		
		LinkedList<Integer> cSize = new LinkedList<Integer>();
		for (Net comp : components) {
			int na = comp.getNumAuthors();
			cSize.add(na);
		}
		
		LinkedList<Integer> cPapers = new LinkedList<Integer>();
		for (Net comp : components) {
			int np = determineNumberOfPapers(comp);
			cPapers.add(np);
		}
		
		System.out.println("\n\nNumber of papers per component distribution");
		Distribution d = new Distribution(cPapers);
		d.printDistr();
		System.out.println("Number of papers per component cummulative distribution... ");
		d.printComplementaryCumulative();
		
		System.out.println("\n\nConnected component size distribution");
		Distribution d2 = new Distribution(cSize);
		d2.printDistr();
		System.out.println("Component Size Complementary cummulative distribution... ");
		d2.printComplementaryCumulative();
		
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("results/componentVector.csv")));
		PrintWriter pw2 = new PrintWriter(new BufferedWriter(new FileWriter("results/nonTrivialCompsVector.csv")));
		
		String header = "CompSize, Links, NumPapers, Trivial, NumJournals, Span, MalePerc, AvgDeg, CC, SW, D, GFD";
		pw.println(header);
		pw2.println(header);
		
		HashMap<Integer, LinkedList<Double>> linkMap = new HashMap<Integer, LinkedList<Double>>();
		HashMap<Integer, LinkedList<Double>> paperMap = new HashMap<Integer, LinkedList<Double>>();
		HashMap<Integer, LinkedList<Double>> swMap = new HashMap<Integer, LinkedList<Double>>();
		
		for (int i = 0; i < components.size(); i++) {
			Net comp = components.get(i);
			
			int size = comp.getNumNodes();
			int numLinks = comp.getNumLinks();
			double malePerc = (double) comp.getNumMaleAuthors() / (double) size;
			int numPapers = determineNumberOfPapers(comp);
			int numJournals = determineNumberOfJournals(comp);
			int span = determineSpan(comp);
			double CC = comp.getCC();
			double SW = comp.getSW();
			double diameter = comp.getDiameter();
			double avgDeg = comp.getAverageDegree();
			double gfd = Math.log(numLinks) / Math.log(size);
			
			boolean trivialComponent = numPapers == 1;
			if (trivialComponent) {
				++numTrivialComponents;
			}
			
			StringBuilder compVectorSb = new StringBuilder();
			compVectorSb.append(size).append(",")
			        .append(numLinks).append(",")
			        .append(numPapers).append(",")
			        .append(trivialComponent).append(",")
			        .append(numJournals).append(",")
			        .append(span).append(",")
			        .append(malePerc).append(",")
			        .append(avgDeg).append(",")
			        .append(CC).append(",")
			        .append(SW).append(",")
			        .append(diameter).append(",")
			        .append(gfd);
			
			String compVector = compVectorSb.toString();
			
			if (size > 1) {
				System.out.println(compVector);
			}
			
			pw.println(compVector);
			
			if (!trivialComponent)
				pw2.println(compVector);
			
			updateMap(linkMap, size, numLinks);
			updateMap(paperMap, size, numPapers);
			updateMap(swMap, size, SW);
			
			// simple check
			//int numIsol = comp.getNumIsolated();
			//if (numIsol != 0) {
			//	System.out.println("Found component with isolated nodes");
			//	System.out.println("Component vector: " + compVector);
			//}
		}
		
		System.out.println("Total number of components = " + components.size());
		System.out.println("Total number of trivial components = " + numTrivialComponents);
	
		pw.close();
		pw2.close();
		
		int npia = determineNumberOfPapersWrittenByIsolatedAuthors(net);
		System.out.println("Number of papers written by isolated authors = " + npia);
		
		System.out.println("Link map.... ");
		dumpMap(linkMap);
		
		System.out.println("\n\nPaper map.... ");
		dumpMap(paperMap);
		
		System.out.println("\n\nSW map.... ");
		dumpMap(swMap);
	}
}
